﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace mc_tester
{
    public class MCPacket
    {
        internal const int SYNC_BYTE = 0x7e;
        internal const int ESCAPE_BYTE = 0x7d;
        internal const int MTU = 256;


        internal const int P_UNKNOWN = 255;

        private List<byte> packet = new List<byte>();
        private List<byte> opacket = new List<byte>();
        int length;

        public MCPacket(List<byte> lb)
        {
            packet = lb;
            length = lb.Count;
            readFramedPacket();
        }

        private bool readFramedPacket()
        {
            bool escaped = false;

            if (packet[0] != SYNC_BYTE)
                return false;

            if (length >= MTU)
            {
                Console.WriteLine("packet too long");
                return false;
            }

            for (int i = 0; i < length; i++)
            {

                if (escaped)
                {
                    if (packet[i] == SYNC_BYTE)
                    {
                        Console.WriteLine("unexpected sync byte");
                        return false;
                    }
                    byte b = packet[i];
                    b ^= 0x20;
                    opacket.Add(b);
                    escaped = false;
                }
                else if (packet[i] == ESCAPE_BYTE)
                {
                    escaped = true;
                }
                else
                    opacket.Add(packet[i]);

                if (packet[i] == SYNC_BYTE && i>0)
                {
                    if (length < 4)
                    {
                        // too-small frames are ignored
                        Console.WriteLine("too-small frame");
                        return false;
                    }
                    
                    int readCrc = (packet[length - 3] & 0xff) | (packet[length - 2] & 0xff) << 8;
                    int computedCrc = Crc.calc(opacket, 1, opacket.Count-4);

                    //System.Console.WriteLine("received: ");
                    //Dump.printPacket(packet);
                    //System.Console.WriteLine(" rcrc: " + System.Convert.ToString(readCrc, 16) + " ccrc: " + System.Convert.ToString(computedCrc, 16));


                    if (readCrc == computedCrc)
                    {
                        Console.WriteLine("good packet");
                        return true;
                    }
                    else
                    {
                        //Console.WriteLine("bad packet");
                    }
                }
            }
            return false;
        }
    }

    internal class Escaper
    {
        internal byte[] escaped;
        internal int escapePtr;
        internal int crc;

        internal Escaper(int length)
        {
            escaped = new byte[2 * length];
            escapePtr = 0;
            crc = 0;
            escaped[escapePtr++] = MCPacket.SYNC_BYTE;
        }

        static private bool needsEscape(int b)
        {
            return b == MCPacket.SYNC_BYTE || b == MCPacket.ESCAPE_BYTE;
        }

        internal virtual void nextByte(int b)
        {
            b = b & 0xff;
            crc = Crc.calcByte(crc, b);
            if (needsEscape(b))
            {
                escaped[escapePtr++] = MCPacket.ESCAPE_BYTE;
                escaped[escapePtr++] = (byte)(b ^ 0x20);
            }
            else
            {
                escaped[escapePtr++] = (byte)b;
            }
        }

        internal virtual void terminate()
        {
            escaped[escapePtr++] = MCPacket.SYNC_BYTE;
        }

        internal List<byte> blist()
        {
            List<byte> ret=new List<byte>();
            for (int i = 0; i < escapePtr; i++)
            {
                ret.Add(escaped[i]);
            }
            return ret;
        }
    }
}
